package org.ivwsqii.singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

/**
 * 单例模式(登记式) 父类
 * 维护一个子类引用集合
 * 创建子类的实例必会先执行父类的构造
 * 通过传入需要获取实例的子类Class对象或Class限定名来获取对应的实例
 * 子类继承AbstractSingleton 就可以实现单例模式
 */
public abstract class AbstractSingleton {
    private static Map<String,AbstractSingleton> registerMap = new HashMap<>();

    public AbstractSingleton() throws Exception {
        String name = this.getClass().getName();
        if (registerMap.containsKey(name)){
            throw new Exception();
        }else{
            synchronized (registerMap){
                if (registerMap.containsKey(name)) {
                    throw new Exception();
                }else {
                    registerMap.put(name,this);
                }
            }
        }
    }

    /**
     *
     * @param tClass
     * @param <T>
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static <T extends AbstractSingleton> T getInstance(final Class<T> tClass) throws IllegalAccessException, InstantiationException {
        String name = tClass.getName();
        if (!registerMap.containsKey(name)) {
            synchronized (registerMap){
                if (!registerMap.containsKey(name)){
                    return tClass.newInstance();
                }
            }
        }
        return (T) registerMap.get(name);
    }

    /**
     * asSubclass其作用是将一个Class对象转换成为指定了泛型的Class对象
     * 首先通过Class.forName（conf.getClassByName就是调用Class.forName）获得Class<?>对象，
     * 但是这个对象是不能放到结果List里面否则会编译错误，List指定了泛型保证插入到List的对象是实现Writable接口的类。
     * 所以就需要进行转换。
     * @return
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static AbstractSingleton getInstance(final String clazzName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if (!registerMap.containsKey(clazzName)){
            Class<? extends AbstractSingleton> aClass = Class.forName(clazzName).asSubclass(AbstractSingleton.class);
            synchronized (registerMap){
                if (!registerMap.containsKey(clazzName)){
                    return aClass.newInstance();
                }
            }
        }
        return registerMap.get(clazzName);
    }

    public static <T extends AbstractSingleton> T getInstance(final Class<T> clazz, Class<?>[] parameterTypes, Object[] initargs)
            throws SecurityException, NoSuchMethodException, IllegalArgumentException,
            InvocationTargetException, InstantiationException, IllegalAccessException{
        String clazzName = clazz.getName();
        if(!registerMap.containsKey(clazzName)){
            synchronized(registerMap){
                if(!registerMap.containsKey(clazzName)){
                    Constructor<T> constructor = clazz.getConstructor(parameterTypes);
                    T instance = constructor.newInstance(initargs);
                    return instance;
                }
            }
        }
        return (T) registerMap.get(clazzName);
    }

}
